﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml;


namespace ArcEngine.Resources
{
	public class TTFFont : ResourceBase
	{
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name"></param>
		public TTFFont(string name) : base(name)
		{
		}




		#region IO routines

		///
		///<summary>
		/// Save the collection to a xml node
		/// </summary>
		///
		public override bool Save(XmlWriter xml)
		{
			if (xml == null)
				return false;

			xml.WriteStartElement("ttffont");
			xml.WriteAttributeString("name", Name);


			base.SaveComment(xml);


			xml.WriteStartElement("file");
			xml.WriteAttributeString("name", FileName);
			xml.WriteEndElement();

			xml.WriteStartElement("size");
			xml.WriteAttributeString("value", Size.ToString());
			xml.WriteEndElement();

			xml.WriteStartElement("style");
			xml.WriteAttributeString("value", Style.ToString());
			xml.WriteEndElement();

			xml.WriteStartElement("color");
			xml.WriteAttributeString("a", color.A.ToString());
			xml.WriteAttributeString("r", color.R.ToString());
			xml.WriteAttributeString("g", color.G.ToString());
			xml.WriteAttributeString("b", color.B.ToString());
			xml.WriteEndElement();

			xml.WriteEndElement();

			return false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="xml"></param>
		/// <returns></returns>
		public override bool Load(XmlNode xml)
		{
			isGenerated = false;

			string desiredname = null;
			int desiredsize = 10 ;
			FontStyle desiredstyle = FontStyle.Regular;


			XmlNodeList nodes = xml.ChildNodes;
			foreach (XmlNode node in nodes)
			{
				if (node.NodeType == XmlNodeType.Comment)
				{
					base.LoadComment(node);
					continue;
				}



				switch (node.Name.ToLower())
				{
					case "file":
					{
						desiredname = node.Attributes["name"].Value;
					}
					break;

					case "size":
					{
						desiredsize = Int32.Parse(node.Attributes["value"].Value);
					}
					break;

					case "style":
					{
						desiredstyle = (FontStyle)Enum.Parse(typeof(FontStyle), node.Attributes["value"].Value, true);
					}
					break;

					case "color":
					{
						color = Color.FromArgb(Int32.Parse(node.Attributes["a"].Value),
												Int32.Parse(node.Attributes["r"].Value),
												Int32.Parse(node.Attributes["g"].Value),
												Int32.Parse(node.Attributes["b"].Value));
					}
					break;

		
					default:
					{
					}
					break;
				}

			}

			// Generate the font
			Generate(desiredname, desiredsize, desiredstyle);

			return true;
		}

		#endregion


		#region Caching


		/// <summary>
		/// Generate the font
		/// </summary>
		/// <param name="name">Name of the font in the ResourceManager</param>
		/// <param name="size">Desired size of the font</param>
		/// <param name="style">Style of the font</param>
		/// <returns>True if font generated, else false</returns>
		public bool Generate(string name, int size, FontStyle style)
		{

			if (name == null || name.Length == 0 || size <= 0)
				return false;
			
			isGenerated = false;
			this.fileName = name;
			this.size = size;
			this.style = style;

			// Clear TileSet
			if (tileSet == null)
				tileSet = new TileSet("private");
			tileSet.Clear();

			// Set the texture
			if (tileSet.Texture == null)
				tileSet.Texture = new Texture("private");
			tileSet.Texture.Clear();


			// Open the sdl font
			byte[] data = ResourceManager.OpenResource(FileName);
			if (data == null)
				return false;

			// GdiFont
			return Generate(new Font(LoadFontFamily(data), size, style));	
		}


		/// <summary>
		/// Generates a font from memory
		/// </summary>
		/// <param name="data">TTF file in memory</param>
		/// <param name="size">Desired size of the font</param>
		/// <param name="style">Style of the font</param>
		/// <returns>True if font generated, else false</returns>
		public bool Generate(byte[] data, int size, FontStyle style)
		{

			if (data == null || Size <= 0)
				return false;

			isGenerated = false;
			this.fileName = "";
			this.size = size;
			this.style = style;

			// Clear TileSet
			if (tileSet == null)
				tileSet = new TileSet("private");
			tileSet.Clear();

			// Set the texture
			if (tileSet.Texture == null)
				tileSet.Texture = new Texture("private");
			tileSet.Texture.Clear();


			// GdiFont
			return Generate(new Font(LoadFontFamily(data), size, style));
		}


		/// <summary>
		/// Internal font generation
		/// </summary>
		/// <returns></returns>
		bool Generate(Font font)
		{
			lineHeight = font.Height;
			isGenerated = false;

			// Final Bitmap
			Bitmap bm = new Bitmap(512, lineHeight, PixelFormat.Format32bppArgb);
			Bitmap tmpbm = null;

			// Offset of the glyph in the texture
			Point pos = Point.Empty;

			// Graphics device
			System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(bm);
			gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
			gfx.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
			gfx.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

			SolidBrush brush = new SolidBrush(color);

			// Get informations for each glyph
			for (byte i = 0; i < 255; i++)
			{
				string c = Convert.ToChar(Convert.ToInt32(i)).ToString();


				// Add the tile to the TileSet
				Tile tile = tileSet.AddTile(i);
				tile.Rectangle = new Rectangle(pos, TextRenderer.MeasureText(gfx, c, font, new Size(int.MaxValue, int.MaxValue), TextFormatFlags.NoPrefix | TextFormatFlags.NoPadding | TextFormatFlags.ExternalLeading)); 


				// Check if enough space left
				if (pos.X + tile.Rectangle.Width >= bm.Width)
				{
					// Relocate the location
					pos.X = 0;
					pos.Y += LineHeight;

					// Resize the bitmap
					tmpbm = new Bitmap(bm.Width, bm.Height + LineHeight);
					System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(tmpbm);
					g.DrawImage(bm, Point.Empty);
					bm = tmpbm;
					gfx = System.Drawing.Graphics.FromImage(bm);
					gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
					gfx.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
					gfx.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

					// Relocate the tile rectangle
					tile.Rectangle = new Rectangle(pos, tile.Rectangle.Size);
				}


				// Draw the glyph to the texture
				TextRenderer.DrawText(gfx, c, font, pos, color, TextFormatFlags.NoPadding);
				//gfx.DrawString(c, font, brush, pos);

				// Offset of the new glyph in the texture
				pos.X += tile.Rectangle.Width;

				//bm.Save(i + ".png", ImageFormat.Png);
			}

			// Close the Sdl font
			font.Dispose();
			gfx.Dispose();


			// Check the texture size (power of two size)
			int powof2 = 1;
			while (powof2 < bm.Height) powof2 <<= 1;
			tmpbm = new Bitmap(bm.Width, powof2);
			gfx = System.Drawing.Graphics.FromImage(tmpbm);
			gfx.DrawImage(bm, Point.Empty);
			bm = tmpbm;


			// Save the image to the texture
			MemoryStream ms = new MemoryStream();
			bm.Save(ms, ImageFormat.Png);
			tileSet.Texture.LoadImage(ms.ToArray());

			//bm.Save("final.png", ImageFormat.Png);

			isGenerated = true;
			return true;
		}


		/// <summary>
		/// Loads font family from byte array
		/// </summary>
		/// <param name="buffer">Memory of the font</param>
		/// <returns></returns>
		private FontFamily LoadFontFamily(byte[] buffer)
		{
			var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
			var pfc = new PrivateFontCollection();
			
			try
			{
				var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
					pfc.AddMemoryFont(ptr, buffer.Length);
			}
			finally
			{
				handle.Free();
			}

			return pfc.Families[0];
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets/sets the rendering style of the font
		/// </summary>
		[Category("Font")]
		[Description("Style of the font")]
		public FontStyle Style
		{
			get
			{
				return style;
			}
		}
		FontStyle style = FontStyle.Regular;


		/// <summary>
		/// Font name (ie: data/fonts/verdana.ttf)
		/// </summary>
		[Category("Font")]
		[Description("Binary file to use")]
		[TypeConverter(typeof(TTFFileEnumerator))]
		public string FileName
		{
			get
			{
				return fileName;
			}
		}
		string fileName;



		/// <summary>
		/// Size of the font
		/// </summary>
		[Category("Font")]
		[Description("Size of the caracters")]
		public int Size
		{
			get
			{
				return size;
			}

		}
		int size = 10;

		/// <summary>
		/// Height of a line of text
		/// </summary>
		[Category("Font")]
		[Description("Height of a line of text in pixel")]
		public int LineHeight
		{
			get
			{
				return lineHeight;
			}
		}
		int lineHeight;
 
         



		/// <summary>
		/// TileSet of the font
		/// </summary>
		[Browsable(false)]
		public TileSet TileSet
		{
			get
			{
				return tileSet;
			}
			protected set
			{
				tileSet = value;
			}
		}
		TileSet tileSet;

		/// <summary>
		/// Font Color
		/// </summary>
		[Category("Color")]
		[Description("Color of the font")]
		public Color Color
		{
			get
			{
				return color;
			}
			set
			{
				color = value;
			}
		}
		Color color = Color.Black;


		/// <summary>
		/// Returns true if t he font is ready to use
		/// </summary>
		public bool IsGenerated
		{
			get
			{
				return isGenerated;
			}
		}
		bool isGenerated = false;

		#endregion

	}
}


